package com.sea.base.pop

import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.PopupWindow
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.Size
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.viewbinding.ViewBinding
import com.sea.base.ext.global.SCREEN_WIDTH
import com.sea.base.ext.global.throwIfDebug
import com.sea.base.ext.obs.lifecycleEventObserver
import com.sea.base.ext.view.MATCH_PARENT
import com.sea.base.ext.view.WRAP_CONTENT
import com.sea.base.ext.view.doOnDetachCanCancel
import com.sea.base.ext.view.requireContext
import com.sea.base.ext.view.requireRootView
import com.sea.base.ui.IUIContext
import java.lang.ref.WeakReference

/**
 * PopupWindow：用于定点式窗口（本类删除了showAtLocation功能）
 *
 * 当前解决的问题：
 * 1.支持根布局layout属性（暂不支持其他属性）
 * 2.绑定activity生命周期，解决未dismiss问题
 * 3.自带生命周期，解决其他对象对Dialog生命周期的感知的问题
 * 4.自带VM，解决与activity共享VM的问题
 * 5.对show前进行生命周期校验
 * 6.实现了ui接口，可一直套娃
 * 7.增加了背景半透明效果
 * 8.对show功能进行拓展，支持更多的位置效果（共81种）
 */
abstract class BasePopupWindow(val ui: IUIContext) : LifecycleOwner, ViewModelStoreOwner,
    IUIContext {
    companion object {
        /**
         * 默认值无背景
         */
        const val BACKGROUND_TYPE_NONE = 0

        /**
         * 一直半透明背景
         */
        const val BACKGROUND_TYPE_FULLSCREEN = 1

        /**
         * Popup之下半透明
         */
        const val BACKGROUND_TYPE_BOTTOM = 2

        /**
         * 相对位置：左上、左中、左下、上中、右上、右中、右下、下中、中间
         */
        const val POP_LEFT_TOP = 0
        const val POP_LEFT_CENTER = 1
        const val POP_LEFT_BOTTOM = 2
        const val POP_TOP_CENTER = 3
        const val POP_RIGHT_TOP = 4
        const val POP_RIGHT_CENTER = 5
        const val POP_RIGHT_BOTTOM = 6
        const val POP_BOTTOM_CENTER = 7
        const val POP_CENTER = 8

        /**
         * 在mvvm里，一般的pop就是普通的v层，所以降级到activity里去
         */
        inline fun <T : ViewBinding> create(ui: IUIContext, crossinline onDialogCreate: BaseViewBindingPopupWindow<T>.() -> Unit) =
            object : BaseViewBindingPopupWindow<T>(ui) {
                override fun onCreate() {
                    onDialogCreate()
                }
            }
    }

    val parentUI = ui

    private val viewModelStore = ViewModelStore()

    private val mLifecycleRegistry = LifecycleRegistry(this)

    private val pop: PopupWindow = PopupWindow()

    var view: View? = null
        private set

    var isCreated = false
        private set
    var isTouchable = true //设置是否可被点击
        set(a) {
            field = a
            pop.isTouchable = a
        }
    var isFocusable = true //设置可否获取焦点
        set(a) {
            field = a
            pop.isFocusable = a
        }
    var isOutsideTouchable = true //设置外部是否可被点击
        set(a) {
            field = a
            pop.isOutsideTouchable = a
        }
    var animationStyle = 0 //设置动画
        set(a) {
            field = a
            pop.animationStyle = a
        }
    var isClippingEnabled = true //允许窗口拓展到屏幕外
        set(a) {
            field = a
            pop.isClippingEnabled = a
        }

    var inputMethodMode = -1 //输入模式
        set(a) {
            field = a
            if (a != -1) {
                pop.inputMethodMode = a
            }
        }

    var width = WRAP_CONTENT
        set(a) {
            field = a
            pop.width = width
        }

    var height = WRAP_CONTENT
        set(a) {
            field = a
            pop.height = height
        }

    /**
     * 经测试，只有SOFT_INPUT_ADJUST_PAN模式有用，并且该模式下布局虽然上移，但内容却被直接裁减掉
     * [isClippingEnabled]看上去可以组合一下，实际上又是新bug
     * 后续考虑使用在activity中添加view的方式来实现PopupWindow功能
     */
    var softInputMode = -1 //软键盘模式
        set(a) {
            field = a
            if (a != -1) {
                pop.softInputMode = a
            }
        }

    var backgroundDrawable = ColorDrawable(Color.TRANSPARENT)
        set(a) {
            field = a
            pop.setBackgroundDrawable(a)
        }

    @DefInfo.BackgroundTypeDef
    private var backgroundType = BACKGROUND_TYPE_NONE

    /**
     * kotlin对注解太不友好了，只能写set方法，不然没有提示
     */
    fun setBackgroundType(@DefInfo.BackgroundTypeDef type: Int) {
        backgroundType = type
        if (isShowing) {
            showBackground(backgroundAttachRootView)
        }
    }

    val isShowing get() = pop.isShowing

    /**
     * 展示的view attach状态回调，当detach时跟随[dismiss]
     */
    private var targetAttachListener: View.OnAttachStateChangeListener? = null

    /**
     * show时附着的view
     */
    private var _targetView: WeakReference<View>? = null
    val targetView get() = _targetView?.get()

    fun setOnDismissListener(listener: ((pop: BasePopupWindow) -> Unit)?) {
        dismissListeners.callListener = listener
    }

    fun setOnTouchListener(listener: ((view: View, event: MotionEvent) -> Boolean)?) {
        pop.setTouchInterceptor(listener)
    }

    /**
     * @param inflateLayout 这个Layout不是parent，仅适合用于inflate
     */
    protected abstract fun createRootView(inflateLayout: ViewGroup): View

    /**
     * dismiss处理
     */
    private val dismissListeners = object : PopupWindow.OnDismissListener {
        var callListener: ((pop: BasePopupWindow) -> Unit)? = null

        override fun onDismiss() {
            removeBackground()
            targetAttachListener?.let {
                targetView?.removeOnAttachStateChangeListener(it)
                _targetView = null
                targetAttachListener = null
            }
            callListener?.invoke(this@BasePopupWindow)
            if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
            }
            if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
                mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
            }
        }
    }

    private val attachedLifecycleObs = lifecycleEventObserver { _, event ->
        when (event) {
            Lifecycle.Event.ON_RESUME -> {
                if (isShowing) {
                    if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
                        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
                    }
                }
            }

            Lifecycle.Event.ON_PAUSE -> {
                if (isShowing) {
                    if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
                        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
                    }
                }
            }

            Lifecycle.Event.ON_STOP -> {
                if (isShowing) {
                    if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
                        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
                    }
                }
            }

            Lifecycle.Event.ON_DESTROY -> {
                dismiss()
                if (lifecycle.currentState != Lifecycle.State.INITIALIZED) {
                    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                }
            }

            else -> {}
        }
    }

    init {
        //设置基本默认值
        isOutsideTouchable = isOutsideTouchable
        isFocusable = isFocusable
        //历史原因，必须有默认背景
        backgroundDrawable = backgroundDrawable
        //关闭时监听
        pop.setOnDismissListener(dismissListeners)
        bindOtherLifecycle(parentUI.lifecycle)
    }

    private fun dispatchCreate() {
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        preCreateRootView()

        //复原xml的布局效果
        val proxyLp = requireRootView.layoutParams
        pop.contentView = view
        pop.width = proxyLp?.width ?: WRAP_CONTENT
        pop.height = proxyLp?.height ?: WRAP_CONTENT

        onCreate()

        isCreated = true
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
    }

    /**
     * 由于弹窗的相对位置复杂，现在重新分为2个阶段来确定相对位置：
     * 首先[PopupWindow]抽象的来说是个矩形，[target]也是一个矩形，现在我们假设在它们中间有一个点（姑且叫它{锚点}）。
     * 这个{锚点}相对于[target]大致有9个方位：左上、左中、左下、右上、右中、右下、上中、下中、中间。
     * 而[PopupWindow]相对于{锚点}也同样有9个方位（注意这是pop相对{锚点}的位置，和{锚点}相对pop正好相反）。
     * 当这两个相对位置确定后，就能得出一个新的位置，比如：
     *      {锚点}左上+pop左上=View的左上角
     *           ---------------
     *           |     pop        |
     *           --------------·(point)
     *                                  -------------------------
     *                                  |                target         |
     *                                  -------------------------
     *      {锚点}右上+pop左上=View的上方并且右对齐
     *                             ---------------
     *                             |     pop        |
     *                            --------------·(point)
     *             -------------------------
     *             |                target         |
     *             -------------------------
     *      {锚点}上中+pop上中=View正上方且居中
     *                     ---------------
     *                     |     pop        |
     *                    -------·-------
     *             -------------------------
     *             |                target         |
     *             -------------------------
     *      ……
     *
     * @param target 目标view
     * @param pointInTargetGravity {锚点}相对于[target]的位置
     * @param popInPointGravity pop相对于{锚点}的位置，默认等于[pointInTargetGravity]（9种比较符合直觉的效果）
     * @param offsetX x轴偏移，单位px
     * @param offsetY y轴偏移，单位px
     * @param observeOnTargetDetach 当view detach时是否跟着dismiss
     */
    @JvmOverloads
    open fun show(
        target: View,
        @DefInfo.PopGravityDef pointInTargetGravity: Int,
        @DefInfo.PopGravityDef popInPointGravity: Int = pointInTargetGravity,
        offsetX: Int = 0,
        offsetY: Int = 0,
        observeOnTargetDetach: Boolean = false,
    ) {
        commonShowCode(target) {
            //根据设置的width、height来测量pop的宽高
            val mWith = getMeasureSpecForSize(pop.width)
            val mHeight = getMeasureSpecForSize(pop.height)
            pop.contentView.measure(mWith, mHeight)

            val popupWidth = pop.contentView.measuredWidth
            val popupHeight = pop.contentView.measuredHeight
            val targetWidth = target.width
            val targetHeight = target.height

            //以showAsDropDown进行计算，此时锚点在左下角
            var realOffsetX = offsetX
            var realOffsetY = offsetY
            //现将锚点移动到左上角，方便计算
            realOffsetY -= targetHeight

            getOffsetValue(pointInTargetGravity, targetWidth, targetHeight).let {
                realOffsetX += it[0]
                realOffsetY += it[1]
            }

            //锚点已经好了，此时pop应该在锚点的右下角
            //先移到左上角，方便计算
            realOffsetX -= popupWidth
            realOffsetY -= popupHeight

            getOffsetValue(popInPointGravity, popupWidth, popupHeight).let {
                realOffsetX += it[0]
                realOffsetY += it[1]
            }

            pop.showAsDropDown(target, realOffsetX, realOffsetY, Gravity.TOP)

            if (observeOnTargetDetach) {
                targetAttachListener = target.doOnDetachCanCancel {
                    this.dismiss()
                }
            }
        }
    }

    /**
     * 指定位置展示，如果可以的话建议使用dialog
     */
    open fun showAtLocation(gravity: Int, x: Int = 0, y: Int = 0) {
        val target = ui.rootView!!
        commonShowCode(target) {
            pop.showAtLocation(target, gravity, x, y)
        }
    }

    private inline fun commonShowCode(target: View, onCustomCodeCall: () -> Unit) {
        //create是在之后，无法用create状态判断
        if (!parentUI.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
            "无法show，当前state：${parentUI.lifecycle.currentState}".throwIfDebug()
            return
        }

        _targetView = WeakReference(target)

        if (!isCreated) {
            dispatchCreate()
        }

        onCustomCodeCall.invoke()
        showBackground(target)

        if (parentUI.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
            mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
        }
    }

    private fun getMeasureSpecForSize(size: Int) = when (size) {
        WRAP_CONTENT -> {
            View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.UNSPECIFIED)
        }

        MATCH_PARENT -> {
            View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY)
        }

        else -> {
            View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY)
        }
    }

    /**
     * 通用方法：获取锚点或者pop的偏移值
     */
    @Size(2)
    private fun getOffsetValue(@DefInfo.PopGravityDef gravity: Int, targetWidth: Int, targetHeight: Int): IntArray {
        var offsetX = 0
        var offsetY = 0
        when (gravity) {
            POP_LEFT_TOP -> {
                //左上，不需要计算（非法值也是默认左上）
            }

            POP_LEFT_CENTER -> {
                //左中，需要将y下移一半
                offsetY += targetHeight / 2
            }

            POP_LEFT_BOTTOM -> {
                //左下，需要将y下移底部
                offsetY += targetHeight
            }

            POP_TOP_CENTER -> {
                offsetX += targetWidth / 2
            }

            POP_RIGHT_TOP -> {
                offsetX += targetWidth
            }

            POP_RIGHT_CENTER -> {
                offsetX += targetWidth
                offsetY += targetHeight / 2
            }

            POP_RIGHT_BOTTOM -> {
                offsetX += targetWidth
                offsetY += targetHeight
            }

            POP_BOTTOM_CENTER -> {
                offsetX += targetWidth / 2
                offsetY += targetHeight
            }

            POP_CENTER -> {
                offsetX += targetWidth / 2
                offsetY += targetHeight / 2
            }
        }
        return intArrayOf(offsetX, offsetY)
    }

    protected abstract fun onCreate()

    fun dismiss() {
        //这个dismiss和Dialog不一样，lifecycle事件写在了dismiss监听里

        pop.dismiss()
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////
    // 半透明背景效果逻辑
    /////////////////////////////////////////////////////////////////////////////////////////////////

    private var backgroundAttachRootView: ViewGroup? = null

    /**
     * 原alpha是透明度，后面的后面一层会被看到，还是这个好
     */
    private var _backgroundView: View? = null
    private val backgroundView: View
        get() {
            if (_backgroundView == null) {
                _backgroundView = View(activity).apply { setBackgroundColor((0x88000000).toInt()) }
            }
            return _backgroundView!!
        }

    /**
     * [backgroundType]
     */
    private fun showBackground(anchor: View?) {
        if (anchor == null) {
            return
        }
        val rootView = anchor.rootView as ViewGroup
        removeBackground()
        if (backgroundAttachRootView !== rootView) {
            backgroundAttachRootView = rootView
        }
        val rootParams = pop.contentView?.rootView?.layoutParams
        when (backgroundType) {
            BACKGROUND_TYPE_BOTTOM -> {
                if (backgroundAttachRootView is FrameLayout && rootParams is WindowManager.LayoutParams) {
                    backgroundAttachRootView?.addView(
                        backgroundView,
                        FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
                            .apply { topMargin = rootParams.y })
                }
            }

            BACKGROUND_TYPE_FULLSCREEN -> {
                backgroundAttachRootView?.addView(backgroundView, MATCH_PARENT, MATCH_PARENT)
            }

            BACKGROUND_TYPE_NONE -> {
            }

            else -> {
            }
        }
    }

    private fun removeBackground() {
        if (_backgroundView != null) {
            backgroundAttachRootView?.removeView(backgroundView)
            backgroundAttachRootView = null
        }
    }

    /**
     * 关联其它的lifecycle 关联的页面关闭，同时会触发dialog的dismiss
     */
    fun bindOtherLifecycle(lifecycle: Lifecycle) {
        lifecycle.addObserver(attachedLifecycleObs)
    }

    /**
     * 如果外界有调用，需要先初始化rootView，否则会崩溃
     */
    fun preCreateRootView() {
        if (view == null) {
            view = createRootView(FrameLayout(requireContext))
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////
    // UIContext的实现
    /////////////////////////////////////////////////////////////////////////////////////////////////
    override val isFinished get() = parentUI.isFinished
    override fun getActivity() = parentUI.activity

    override fun getContext() = parentUI.context
    override fun getLayoutInflater() = parentUI.layoutInflater

    /**
     * 暂时使用activity的
     */
    override val currentFragmentManager get() = parentUI.currentFragmentManager
    override val rootView get() = view
    override val realDelegate get() = this

    override fun requireViewLifecycle() = lifecycle

    override fun viewLifecycleWithCallback(run: (Lifecycle?) -> Unit) {
        run.invoke(lifecycle)
    }

    override fun startActivity(intent: Intent, options: Bundle?) =
        parentUI.startActivity(intent, options)

    override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) =
        parentUI.startActivityForResult(intent, requestCode, options)

    override fun <I, O> registerForActivityResult(
        contract: ActivityResultContract<I, O>,
        callback: ActivityResultCallback<O>
    ): ActivityResultLauncher<I> =
        parentUI.registerForActivityResult(contract, callback)

    override fun <I, O> registerForActivityResult(
        contract: ActivityResultContract<I, O>,
        registry: ActivityResultRegistry,
        callback: ActivityResultCallback<O>
    ): ActivityResultLauncher<I> =
        parentUI.registerForActivityResult(contract, registry, callback)

    override fun getLifecycle() = mLifecycleRegistry

    override fun getViewModelStore() = viewModelStore
}